﻿/*****************************************************
 * File:            OTCG_EgdeCoverage.cs
 * Description:     Edge Coverage strategy
 * By:              Ali Khalili (khalili DOT ir AT gmail DOT com)
 * Project:         Automata Learning
 * Requirements:    
 * Change log:
 *          -Jan 2013: initial version
 *          -May 2013: I rewrite the code!
 * License:
 *****************************************************/
using System;
using System.Collections.Generic;

//using EnGine.Core;
//using AIDE.Base;
using SMLib.Base;
using MBT.GraphAlgorithms;
//

namespace MBT.Conformance.OfflineTesting
{
    /// <summary>
    /// Offline Test Case generator based on Edge coverage strategy
    /// </summary>
    public class OTCG_EdgeCoverage<STATETYPE, TRANSITIONTYPE> 
    {
        Directed_CPP<STATETYPE, TRANSITIONTYPE> tourFinder;
        IDiGraph<STATETYPE, TRANSITIONTYPE> graph;
        List<TRANSITIONTYPE> tour;
        BranchingState<STATETYPE> branchDetectorHandler;
        GraphShortestPath<STATETYPE, TRANSITIONTYPE> sp;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="st"></param>
        /// <returns></returns>
        bool branchDetectorHandlerDef(STATETYPE st)
        {
            return false;
        }

        /// <summary>
        /// Constructor
        /// <param name="graph">the graph</param>
        /// <param name="branchDetectorHandler">the branch detector handler (if null, the default is that there is no branch)</param>
        /// </summary>
        public OTCG_EdgeCoverage(IDiGraph<STATETYPE, TRANSITIONTYPE> graph, BranchingState<STATETYPE> branchDetectorHandler)
        {
            this.graph = graph;
            if (branchDetectorHandler == null)
                this.branchDetectorHandler = branchDetectorHandlerDef;
            else
                this.branchDetectorHandler = branchDetectorHandler;

        }

        /// <summary>
        /// Generate Test Cases
        /// </summary>
        public void GenerateTestCases()
        {
            tourFinder = new Directed_CPP<STATETYPE, TRANSITIONTYPE>(graph, null);
            tour = tourFinder.ComputeTransitionTour();

            sp = new GraphShortestPath<STATETYPE, TRANSITIONTYPE>(graph, null, null);
            sp.ComputeShortestPathes();
        }

        #region IOfflineTCG Members
        const double MAX_LENGHT_RATIO = 1.5;

        /// <summary>
        /// Enumerate generated test cases
        /// </summary>
        /// <returns>test cases</returns>
        public IEnumerable<List<TRANSITIONTYPE>> EnumerateTestCases()
        {
            STATETYPE currentState = graph[0];
            List<TRANSITIONTYPE> shortestPath = null;
            List<TRANSITIONTYPE> testCase = null;
            for (int i = 0; i < tour.Count; i++)
            {
                //* in the begining: initialize
                if (testCase == null && i == 0)
                {
                    shortestPath = new List<TRANSITIONTYPE>();
                    testCase = new List<TRANSITIONTYPE>();
                }
                else if (testCase == null)
                {
                    shortestPath = sp.ShortestPath(graph.IndexOf(currentState));
                    testCase = new List<TRANSITIONTYPE>(shortestPath);
                }

                if (tour[i] == null)//is ResetTransition<TRANSITIONTYPE>)
                {
                    currentState = graph[0];
                    if (shortestPath.Count == testCase.Count)
                    {
                        //* this is not efficient but may very rarely happen!
                        testCase = null;
                        continue;
                    }
                    else
                    {
                        yield return testCase;
                        testCase = null;
                        continue;
                    }
                }

                testCase.Add(tour[i]);
                currentState = graph.DestinationOf(tour[i]);

                //* find shortest path from initial node to the current node
                if (testCase.Count > ((MAX_LENGHT_RATIO * ((double)graph.Size))) || branchDetectorHandler(currentState))
                {
                    yield return testCase;
                    testCase = null;
                    continue;
                }

                if (i == tour.Count - 1)
                    yield return testCase;
            }
        }

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <returns></returns>
        //public List<List<ITransition>> GenerateTestSuit()
        //{

        //    throw new NotImplementedException();
        //}

        #endregion
    }

    //    /// <summary>
    //    /// Offline Testcase Generator (Edge Coverage Strategy)
    //    /// </summary>
    //    public class OTCG_EdgeCoverage : IOfflineTCG
    //    {
    //        TranSys testModel;
    //        MealyCostProb mcp;
    //        public OTCG_EdgeCoverage(TranSys model)
    //        {
    //            testModel = model;
    //            mcp = new MealyCostProb(new MealyNondeterminismDetector());
    //        }

    //        List<NDGraphTraversalStep<string, int>> tour;
    //        GraphShortestPath_OLD fp;

    //        /// <summary>
    //        /// Compute transition tour (CPP) AND shortest path of all edges from initial state (Dijkstra)
    //        /// </summary>
    //        public void GenerateTestCases()
    //        {
    //            DirectedCPP_OLD G = new DirectedCPP_OLD(testModel);
    //            //* find the CPT
    //            G.solve();
    //            //* print it, starting from vertex 0
    //            tour = G.printCPT(0);

    //            for (int i = 0; i < tour.Count; i++)
    //            {
    //                if (tour[i].edgeLabel == null)
    //                {
    //                    for (int j = 0; j < tour.Count; j++)
    //                    {
    //                        Console.Write("(" + tour[j].edgeLabel + "," + tour[j].nodeLabel + ")");
    //                    }
    //                    Visualization.Visualization v = new Visualization.Visualization(testModel);
    //                    v.ShowDialog();

    //                    throw new Exception("Tour is not correct!");
    //                }
    //            }

    //            //Console.WriteLine("Cost = " + Globally.cost() + ", |Tour|=" + tour.Count + ", |states|=" + testModel.Count);

    //            fp = new GraphShortestPath_OLD(testModel, null);
    //            fp.ComputeShortestPathes(0);
    //        }

    //        public int L()
    //        {
    //            return tour.Count;
    //        }

    //        public IEnumerable<List<NDGraphTraversalStep<string, int>>> EnumerateTestCases()
    //        {
    //            MealyNondeterminismDetector ndDetector = new MealyNondeterminismDetector();
    //            MealyMachine.Mealy_State currentNode = (MealyMachine.Mealy_State)testModel[0];
    //            List<NDGraphTraversalStep<string, int>> testCase;
    //            int testLen;
    //            for (int i = 0; i < tour.Count; i++)
    //            {
    //                //testCase = new List<NDGraphTraversalStep<string, int>>();

    //                /// Find the shortest path from inital node to this branch node!
    //                /// tour[i - 1]: the node that reached in the end of previous edge
    //                testCase = fp.ShortestPath(i == 0 ? 0 : tour[i - 1].nodeLabel);
    //                testLen = 0;
    //                /// Then, follow the path from tour
    //                do
    //                {
    //                    if (tour[i].edgeLabel == "Reset")
    //                    {
    //                        /// This is the point we should reset the system. So, we consider it as the begining of next test case
    //                        yield return testCase;
    //                        /// Set currentNode for next test case
    //                        currentNode = (MealyMachine.Mealy_State)testModel[0];
    //                        break;
    //                    }

    //                    string input = tour[i].edgeLabel.Substring(0, tour[i].edgeLabel.IndexOf("/"));
    //                    string cop = tour[i].edgeLabel.Substring(tour[i].edgeLabel.IndexOf("/") + 1,
    //                                        tour[i].edgeLabel.Length - tour[i].edgeLabel.IndexOf("/") - 1);

    //                    if (testLen > testModel.Count)
    //                    {
    //                        /// This is the branch point AND should be considered again (as the begining of the next test case)
    //                        i--;
    //                        yield return testCase;
    //                        break;
    //                    }

    //                    /// Begining of next test case [end of this one!]
    //                    if ((testLen > 0) && (currentNode.ListOfAllNextNodes(new StringSymbol(input)).Count > 1))
    //                    {
    //                        /// This is the branch point AND should be considered again (as the begining of the next test case)
    //                        i--;
    //                        yield return testCase;
    //                        break;
    //                    }
    //                    else
    //                    {
    //                        testCase.Add(tour[i]);
    //                        /// If it  is a branch point, add all other accepting outputs!
    //                        if (testLen == 0 && (currentNode.ListOfAllNextNodes(new StringSymbol(input)).Count > 1))
    //                        {
    //                            testCase[testCase.Count - 1].MakeItBranch();
    //                            foreach (MealyMachine.Mealy_Transition t in currentNode.Transitions)
    //                            {
    //                                if (t.inLabel.ToString() == input)
    //                                    testCase[testCase.Count - 1].OtherAcceptingEdges.Add(t.outLabel.ToString());
    //                            }
    //                        }

    //                        /// As we assumed that the machine is identifiable, there is a unique node with a specifc input output
    //                        currentNode = currentNode.NextNode(new StringSymbol(input), new StringSymbol(cop));

    //                        testLen++;
    //                    }
    //                    i++;

    //                    /// End of tour!
    //                    if (i == tour.Count)
    //                    {
    //                        yield return testCase;
    //                        break;
    //                    }
    //                } while (true);

    //            }
    //        }

    //        #region IOfflineTCG Members


    //        public List<List<NDGraphTraversalStep<string, int>>> GenerateTestSuit()
    //        {
    //            throw new NotImplementedException();
    //        }

    //        #endregion
    //    }

}
